/**
 * 
 */
package at.ac.tuwien.vpsbcm.spielzeugfabrik.space;

import java.awt.EventQueue;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.Serializable;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.ArrayList;

import org.mozartspaces.capi3.AnyCoordinator;
import org.mozartspaces.capi3.CountNotMetException;
import org.mozartspaces.capi3.FifoCoordinator;
import org.mozartspaces.capi3.LabelCoordinator;
import org.mozartspaces.core.Capi;
import org.mozartspaces.core.Entry;
import org.mozartspaces.core.ContainerReference;
import org.mozartspaces.core.DefaultMzsCore;
import org.mozartspaces.core.MzsConstants;
import org.mozartspaces.core.MzsCore;
import org.mozartspaces.core.MzsCoreException;
import org.mozartspaces.core.TransactionReference;
import org.mozartspaces.notifications.Notification;
import org.mozartspaces.notifications.NotificationListener;
import org.mozartspaces.notifications.NotificationManager;
import org.mozartspaces.notifications.Operation;

import at.ac.tuwien.vpsbcm.spielzeugfabrik.BauteilEnum;
import at.ac.tuwien.vpsbcm.spielzeugfabrik.gui.GUIMain;
import at.ac.tuwien.vpsbcm.spielzeugfabrik.space.utils.ContainerUtils;
import at.ac.tuwien.vpsbcm.spielzeugfabrik.space.Auftrag;

/**
 * @author Andreas Wolf
 *
 */
public class GUISpace extends GUIMain implements Runnable, NotificationListener {

	
	private URI spaceurl;
	private MzsCore core;
	private Capi capi;
	private ContainerReference BauteilContainer;
	private ContainerReference BauteilVerbautContainer;
	private ContainerReference LokomotivenContainer;
	private ContainerReference LokomotivenGetestetContainer;
	private ContainerReference LokomotivenFehlerhaftContainer;
	private ContainerReference AusgeliefertContainer;
	private ContainerReference ProduzentContainer;
	private ContainerReference AuftragContainer;
	private NotificationManager notifManager;
	private Notification writeNotif;
	/**
	 * 
	 */
	public GUISpace(URI uri) {
		this.spaceurl = uri;
		this.GUITitle = "Space GUI";
		initialize ();
		frmGui.addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosed(WindowEvent arg0) {
				try {
					writeNotif.destroy();
					notifManager.shutdown();
					capi.shutdown(null);
				} catch (MzsCoreException e) {
					e.printStackTrace();
				}
			}
		});
	}

	protected void starteProduzent(int id, BauteilEnum bauteil, int anzahl, int fehlerrate) {
		Produzent p;
		TransactionReference tx;
		
		p = new Produzent(capi, BauteilContainer, lastProduzentId, (BauteilEnum)comboBauteil.getSelectedItem(), anzahl, fehlerrate);
		try {
			tx = capi.createTransaction(1000, spaceurl);
			ArrayList<Integer> entry = capi.take(ProduzentContainer, LabelCoordinator.newSelector("ProduzentID"), 0, null);
			int producerid = entry.get(0).intValue() + 1;
			System.out.format("set ProduzentID: %d\n", producerid);
			capi.write(new Entry(new Integer(producerid), LabelCoordinator.newCoordinationData("ProduzentID")), ProduzentContainer, 0, null);
			capi.commitTransaction(tx);
		} catch (MzsCoreException e) {
			e.printStackTrace();
		}
		System.out.println("Start Thread with id" + (lastProduzentId-1));		
		Thread t = new Thread(p);
		t.start();
	}
	
	protected void neuerAuftrag(int anzahl, boolean selected, String color) {
		try {
			int AuftragID;
			TransactionReference tx = capi.createTransaction(1000, spaceurl);
			try {
				AuftragID = ((Integer)capi.take(ProduzentContainer, LabelCoordinator.newSelector("AuftragID", 1), 0, tx).get(0)).intValue();
				AuftragID++;
			} catch (CountNotMetException e) {
				AuftragID = 0;				
			}
			capi.write(new Entry(AuftragID, LabelCoordinator.newCoordinationData("AuftragID")), ProduzentContainer);
			Auftrag auftrag = new Auftrag(AuftragID, selected, color, anzahl);
			capi.write( new Entry( auftrag
								 , LabelCoordinator.newCoordinationData(Integer.toString(AuftragID))
					             , AnyCoordinator.newCoordinationData("ungebaut")
					             , AnyCoordinator.newCoordinationData("unlackiert")
					             , AnyCoordinator.newCoordinationData("lackiert")
					             , AnyCoordinator.newCoordinationData("getestet")
					             , AnyCoordinator.newCoordinationData("fehlerhaft")
					             , FifoCoordinator.newCoordinationData()
					             )
			          , AuftragContainer
			          , 0
			          , tx
			          );
			capi.commitTransaction(tx);
		} catch (MzsCoreException e) {
			e.printStackTrace();
		}
		
	}
	
	public void run() {
		int transDuration = 10000;
		TransactionReference tx;
		ContainerUtils cUtil;
		try {
			if (spaceurl == null) {
				core = DefaultMzsCore.newInstance();
				spaceurl = core.getConfig().getSpaceUri();
			} else {
				core = DefaultMzsCore.newInstanceWithoutSpace();
			}
			capi = new Capi(core);
			notifManager = new NotificationManager(core);
			cUtil = new ContainerUtils (capi, spaceurl);
			BauteilContainer = cUtil.InitBauteileContainer();
			BauteilVerbautContainer = cUtil.InitBauteileVerbautContainer();
			LokomotivenContainer = cUtil.InitLokomotivenContainer();
			LokomotivenGetestetContainer = cUtil.InitLokomotivenGetestetContainer();
			LokomotivenFehlerhaftContainer = cUtil.InitLokomotivenFehlerhaftContainer();
			AusgeliefertContainer = cUtil.InitAusgeliefertContainer();
			AuftragContainer = cUtil.InitAuftragContainer();
			ProduzentContainer = cUtil.InitProduzentContainer();
			
			tx = capi.createTransaction(transDuration, spaceurl);
			try {
				ArrayList<Integer> entry = capi.read(ProduzentContainer, LabelCoordinator.newSelector("ProduzentID"), 0, tx);
				this.lastProduzentId = (entry.get(0).intValue());
				capi.commitTransaction(tx);
				System.out.format ("got ProduzentID: %d\n", this.lastProduzentId);
			} catch (CountNotMetException e) {
				capi.write(new Entry(new Integer(0), LabelCoordinator.newCoordinationData("ProduzentID")), ProduzentContainer, 0, tx);
				capi.commitTransaction(tx);
				System.out.format ("Init ProduzentID: %d\n", 0);
			}
			
			
			tx = capi.createTransaction(transDuration, spaceurl);
			ArrayList<Bauteil> teile;
			capi.lockContainer(BauteilContainer, tx);
			teile = capi.read(BauteilContainer, FifoCoordinator.newSelector(MzsConstants.Selecting.COUNT_ALL) , 0, tx);
			for (Bauteil teil : teile) {
				BauteileTableModelobj.addRow(teil.getArray());
			}
			capi.lockContainer(BauteilVerbautContainer, tx);
			teile = capi.read(BauteilVerbautContainer, FifoCoordinator.newSelector(MzsConstants.Selecting.COUNT_ALL) , 0, tx);
			for (Bauteil teil : teile) {
				VerbauteBauteileTableModelobj.addRow(teil.getArray());
			}
			ArrayList<Lokomotive> loks;
			capi.lockContainer(LokomotivenContainer, tx);
			loks = capi.read(LokomotivenContainer, FifoCoordinator.newSelector(MzsConstants.Selecting.COUNT_ALL), 0, tx);
			for (Lokomotive lok : loks) {
				FertigeLoksListModelobj.addElement(lok.toStringFertige());
			}
			capi.lockContainer(LokomotivenGetestetContainer, tx);
			loks = capi.read(LokomotivenGetestetContainer, FifoCoordinator.newSelector(MzsConstants.Selecting.COUNT_ALL), 0, tx);
			for (Lokomotive lok : loks) {
				GetesteteLoksListModelobj.addElement(lok.toStringFertige());
			}
			capi.lockContainer(LokomotivenFehlerhaftContainer, tx);
			loks = capi.read(LokomotivenFehlerhaftContainer, FifoCoordinator.newSelector(MzsConstants.Selecting.COUNT_ALL), 0, tx);
			for (Lokomotive lok : loks) {
				FehlerhafteLoksListModelobj.addElement(lok.toStringFertige());
			}
			capi.lockContainer(AusgeliefertContainer, tx);
			loks = capi.read(AusgeliefertContainer, FifoCoordinator.newSelector(MzsConstants.Selecting.COUNT_ALL), 0, tx);
			for (Lokomotive lok : loks) {
				AusgelieferteLoksListModelobj.addElement(lok.toStringAusgelieferte());
			}
			
			capi.lockContainer(AuftragContainer, tx);
			ArrayList<Auftrag> auftrags = capi.read(AuftragContainer, FifoCoordinator.newSelector(MzsConstants.Selecting.COUNT_ALL), 0, tx);
			for (Auftrag a : auftrags) {
				AuftragListModelobj.addElement(a.toString());
			}
			
			notifManager.createNotification(BauteilContainer, this, Operation.WRITE, Operation.DELETE, Operation.TAKE);

			notifManager.createNotification(BauteilVerbautContainer, new NotificationListener () {
				public void entryOperationFinished (Notification source, Operation operation, List<? extends Serializable> entries) {
					if (operation.equals(Operation.WRITE)) {
						for (Serializable entry : entries) {
							VerbauteBauteileTableModelobj.addRow(((Bauteil)((Entry) entry).getValue()).getArray());
						}
					} else {
						Object[] arr;
						int row;
						for (Serializable entry : entries) {
							arr = ((Bauteil)((Entry)entry).getValue()).getArray();
							row = VerbauteBauteileTableModelobj.getBauteilRow(((Integer)arr[0]).intValue(), ((Integer)arr[1]).intValue());
							VerbauteBauteileTableModelobj.removeRow(row);
						}
					}
					
				}
			}, Operation.WRITE, Operation.TAKE, Operation.DELETE);

			notifManager.createNotification(LokomotivenContainer, new NotificationListener () {
				public void entryOperationFinished (Notification source, Operation operation, List<? extends Serializable> entries) {
					if (operation.equals(Operation.WRITE)) {
						String zeile = new String();
						zeile = ((Lokomotive)((Entry)entries.get(0)).getValue()).toStringFertige();
						FertigeLoksListModelobj.addElement(zeile);
					} else {
						FertigeLoksListModelobj.removeElement(((Lokomotive)entries.get(0)).toStringFertige());
					}
					
				}
			}, Operation.WRITE, Operation.TAKE, Operation.DELETE);
			notifManager.createNotification(LokomotivenGetestetContainer, new NotificationListener () {
				public void entryOperationFinished (Notification source, Operation operation, List<? extends Serializable> entries) {
					if (operation.equals(Operation.WRITE)) {
						String zeile = new String();
						zeile = ((Lokomotive)((Entry)entries.get(0)).getValue()).toStringFertige();
						GetesteteLoksListModelobj.addElement(zeile);
					} else {
						GetesteteLoksListModelobj.removeElement(((Lokomotive)entries.get(0)).toStringFertige());
					}
					
				}
			}, Operation.WRITE, Operation.TAKE, Operation.DELETE);	
			notifManager.createNotification(LokomotivenFehlerhaftContainer, new NotificationListener () {
				public void entryOperationFinished (Notification source, Operation operation, List<? extends Serializable> entries) {
					if (operation.equals(Operation.WRITE)) {
						String zeile = new String();
						zeile = ((Lokomotive)((Entry)entries.get(0)).getValue()).toStringFertige();
						FehlerhafteLoksListModelobj.addElement(zeile);
					} else {
						FehlerhafteLoksListModelobj.removeElement(((Lokomotive)entries.get(0)).toStringFertige());
					}
					
				}
			}, Operation.WRITE, Operation.TAKE, Operation.DELETE);			
			notifManager.createNotification(AusgeliefertContainer, new NotificationListener () {
				public void entryOperationFinished (Notification source, Operation operation, List<? extends Serializable> entries) {
					if (operation.equals(Operation.WRITE)) {
						String zeile = new String();
						zeile = ((Lokomotive)((Entry)entries.get(0)).getValue()).toStringAusgelieferte();
						AusgelieferteLoksListModelobj.addElement(zeile);
					} else {
						AusgelieferteLoksListModelobj.removeElement(((Lokomotive)entries.get(0)).toStringAusgelieferte());
					}
					
				}
			}, Operation.WRITE, Operation.TAKE, Operation.DELETE);
			notifManager.createNotification(AuftragContainer, new NotificationListener () {
				public void entryOperationFinished (Notification source, Operation operation, List<? extends Serializable> entries) {
					if (operation.equals(Operation.WRITE)) {
						for (Serializable entry : entries) {
							Auftrag a = (Auftrag)((Entry)entry).getValue();
							String as = a.getAuftragID().toString()+",";
							for (int i=0; i<AuftragListModelobj.getSize(); i++) {
								if (AuftragListModelobj.get(i).startsWith(as)) {
									AuftragListModelobj.remove(i);
									break;
								}
							}
							AuftragListModelobj.addElement(a.toString());
						}
					} else {
						for (Serializable entry : entries) {
							Auftrag a = (Auftrag)((Entry)entry).getValue();
							/* find right auftrag */
							String as = a.getAuftragID().toString()+",";
							for (int i=0; i<AuftragListModelobj.getSize(); i++) {
								if (AuftragListModelobj.get(i).startsWith(as)) {
									AuftragListModelobj.remove(i);
									break;
								}
							}
						}
					}
					
				}
			}, Operation.WRITE, Operation.TAKE, Operation.DELETE);				
			capi.commitTransaction(tx);
		} catch (MzsCoreException e) {
			e.printStackTrace();			
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		
		try {
			this.frmGui.setVisible(true);
		} catch (Exception e) {
			e.printStackTrace();
		}		
	}

	public void entryOperationFinished(Notification arg0, Operation arg1,
			List<? extends Serializable> arg2) {
		Object[] arr;
		int row;
		System.out.format("Got new notification: %s\n", arg1);
		if (arg1.equals(Operation.WRITE)) {
			@SuppressWarnings("unchecked")
			List<Entry> entries = ((List<Entry>)arg2);
			for (Entry entry : entries) {
				try {
					arr = ((Bauteil)entry.getValue()).getArray();
					// check if entry already exists in table
					row = BauteileTableModelobj.getBauteilRow (((Integer)arr[0]).intValue(), ((Integer)arr[1]).intValue());
					if (row == -1){
						BauteileTableModelobj.addRow(arr);
					} else {
							BauteileTableModelobj.setValueAt(arr[0], row, 0);
							BauteileTableModelobj.setValueAt(arr[1], row, 1);
							BauteileTableModelobj.setValueAt(arr[2], row, 2);
							BauteileTableModelobj.setValueAt(arr[3], row, 3);
							BauteileTableModelobj.fireTableRowsUpdated(row, row);
					}
				} catch (ArrayIndexOutOfBoundsException e) {
					
				}
			}
		} else if (arg1.equals(Operation.DELETE) || arg1.equals(Operation.TAKE)) {
			
			@SuppressWarnings("unchecked")
			List<Bauteil> teile = ((List<Bauteil>)arg2);
			for (Bauteil teil : teile) {
				arr = teil.getArray();
				row = BauteileTableModelobj.getBauteilRow (((Integer)arr[0]).intValue(), ((Integer)arr[1]).intValue());
				if (row != -1) {
					BauteileTableModelobj.removeRow(row);
				}
			}
			BauteileTableModelobj.fireTableDataChanged();
		}
	}	

	/**
	 * @param args
	 * @throws URISyntaxException 
	 */
	public static void main(String[] args) {
		GUISpace gui;
		if (args.length == 1) {
			try {
				gui = new GUISpace(new URI(args[0]));
				EventQueue.invokeLater(gui);
			} catch (URISyntaxException e) {
				e.printStackTrace();
			}
		} else {
			gui = new GUISpace(null);
			EventQueue.invokeLater(gui);
		}
		
	}

}
